package com.ideal.frame.prefix.service;

import com.ideal.frame.institutions.entity.InstitutionsOrganizationTb;
import com.ideal.frame.institutions.entity.QInstitutionsOrganizationTb;
import com.ideal.frame.jpa.DSLPageRequest;
import com.ideal.frame.jpa.JpaRepositoryImpl;
import com.ideal.frame.node.entity.NodeTb;
import com.ideal.frame.node.entity.QNodeTb;
import com.ideal.frame.node.service.NodeSyncEventService;
import com.ideal.frame.node.service.NodeSyncService;
import com.ideal.frame.prefix.dto.PrefixNextTbForm;
import com.ideal.frame.prefix.entity.PrefixNetxTb;
import com.ideal.frame.prefix.entity.PrefixSelfTb;
import com.ideal.frame.prefix.entity.QPrefixNetxTb;
import com.ideal.frame.prefix.form.search.PrefixSyncParams;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.Date;
import java.util.List;
import java.util.Optional;

/**
 * @Auther: liudingsheng
 * @Date: 2021/8/27
 * @Description:
 */
@Service
public class PrefixNextTbService {

    @PersistenceContext
    protected EntityManager entityManager;
    private JpaRepositoryImpl<PrefixNetxTb, Long> prefixNetxTbLongJpaRepository;
    private JpaRepositoryImpl<NodeTb, Long> nodeTbLongJpaRepository;
    private JpaRepositoryImpl<PrefixSelfTb, Long> prefixSelfTbLongJpaRepository;
    private JpaRepositoryImpl<InstitutionsOrganizationTb, Long> ioRepo;
    @Autowired
    private NodeSyncEventService nodeSyncEventService;

    QPrefixNetxTb qPrefixNetxTb = QPrefixNetxTb.prefixNetxTb;
    QNodeTb qNodeTb = QNodeTb.nodeTb;
    QInstitutionsOrganizationTb qInstitutionsOrganizationTb = QInstitutionsOrganizationTb.institutionsOrganizationTb;
    @Value("${node.level}")
    private int level;
    @Value("${node.prefix}")
    private String nodePrefix;
    @Autowired
    private NodeSyncService nodeSyncService;

    @PostConstruct
    public void initRepo() {
        prefixNetxTbLongJpaRepository = new JpaRepositoryImpl<>(PrefixNetxTb.class, entityManager);
        nodeTbLongJpaRepository = new JpaRepositoryImpl<>(NodeTb.class, entityManager);
        prefixSelfTbLongJpaRepository = new JpaRepositoryImpl<>(PrefixSelfTb.class, entityManager);
        ioRepo = new JpaRepositoryImpl<>(InstitutionsOrganizationTb.class, entityManager);
    }

    /**
     * 查询待同步的数据
     *
     * @param params
     * @return
     */
    public List<PrefixNetxTb> findBySyncTime(PrefixSyncParams params) {
        JPAQuery<PrefixNetxTb> query = prefixNetxTbLongJpaRepository.selectFrom(qPrefixNetxTb)
                .where(qPrefixNetxTb.syncSourcePrefix.eq(params.getPrefix()))
                .where(qPrefixNetxTb.issync.isFalse())
                .orderBy(qPrefixNetxTb.updatedate.asc());
        if (!StringUtils.isEmpty(params.getSyncTime())) {
            query.where(qPrefixNetxTb.updatedate.gt(params.getSyncTime()));
        }
        return query.fetch();
    }

    /**
     * 查询待同步的数据
     *
     * @param params
     * @return
     */
    public List<PrefixNetxTb> findOtherBySyncTime(PrefixSyncParams params) {
        JPAQuery<PrefixNetxTb> query = prefixNetxTbLongJpaRepository.selectFrom(qPrefixNetxTb)
                .where(qPrefixNetxTb.syncSourcePrefix.ne(params.getPrefix()))
                .orderBy(qPrefixNetxTb.updatedate.asc());
        if (!StringUtils.isEmpty(params.getSyncTime())) {
            query.where(qPrefixNetxTb.updatedate.gt(params.getSyncTime()));
        }
        return query.fetch();
    }

    /**
     * 同步前缀数据
     *
     * @param list
     */
    @Transactional(rollbackFor = Exception.class)
    public void syncPrefixData(List<PrefixNetxTb> list) {
        if (list != null && list.size() > 0) {
            list.forEach(t -> {
                long id = t.getId();
                PrefixNetxTb prefix = findByPrefixSyncId(id, t.getSyncSourcePrefix());
                t.setSyncId(id);
                if (prefix != null) {
                    t.setId(prefix.getId());
                } else {
                    t.setId(null);
                }
                t.setIssync(Boolean.TRUE);
                saveAndFlush(t);
            });
            nodeSyncEventService.saveSyncEvent(1, list.get(list.size() - 1).getUpdatedate(), nodePrefix);
        }
    }

    public PrefixNetxTb getByNodeId(Integer nodeId) {
        Optional<PrefixNetxTb> optional = prefixNetxTbLongJpaRepository.findOne(qPrefixNetxTb.nodeid.eq(nodeId));
        return optional.isPresent() ? optional.get() : null;
    }

    public List<PrefixNetxTb> getAllByOrgId(String orgId) {
        JPAQuery<PrefixNetxTb> prefixNetxTbJPAQuery = prefixNetxTbLongJpaRepository.selectFrom(qPrefixNetxTb);
/*        if(orgId != null){
            prefixNetxTbJPAQuery = prefixNetxTbJPAQuery.where(qPrefixNetxTb.institutionsOrganizationTb.gnrlid.eq(orgId));
        }*/
 /*       List<PrefixNetxTb> prefixNetxTbs = prefixNetxTbJPAQuery
                .where(nodeTbLongJpaRepository.select(qNodeTb.prefix).from(qNodeTb).where(qNodeTb.isdelete.isFalse().and(qPrefixNetxTb.id.eq(qNodeTb.prefix.id))).notExists()).fetch();*/
        prefixNetxTbJPAQuery.where(qPrefixNetxTb.syncSourcePrefix.eq(nodePrefix));
        List<PrefixNetxTb> prefixNetxTbs = prefixNetxTbJPAQuery.fetch();
        return prefixNetxTbs;
    }

    public Page<PrefixNetxTb> getPage(PrefixNextTbForm prefixNextTbForm) {
        DSLPageRequest pageRequest = prefixNextTbForm.asDslPageRequest();

        if (prefixNextTbForm.getPrefix() != null) {
            pageRequest.addWhere(qPrefixNetxTb.prefixNext.like("%" + prefixNextTbForm.getPrefix() + "%"));
        }

        if (prefixNextTbForm.getId() != null) {
            pageRequest.addWhere(qPrefixNetxTb.id.eq(prefixNextTbForm.getId()));
        }
        pageRequest.addWhere(qPrefixNetxTb.isdelete.isFalse());
        return prefixNetxTbLongJpaRepository.findAll(pageRequest);
    }

    @Transactional(rollbackFor = Exception.class)
    public void savePrefixNetxTb(PrefixNetxTb prefixNetxTb) {
        Date now = new Date();
        PrefixNetxTb prefix = findByPrefix(prefixNetxTb.getPrefixNext(), prefixNetxTb.getId());
        Assert.isTrue(prefix == null, "当前前缀已存在，请勿重复新增");
        if (prefixNetxTb.getId() == null) {
            prefixNetxTb.setInsertdate(now);
            prefixNetxTb.setSyncSourcePrefix(nodePrefix);
            prefixNetxTb.setIssync(Boolean.FALSE);
        } else {
            if (level == 0 || level == -1) {
                PrefixNetxTb prefixtb = prefixNetxTbLongJpaRepository.getOne(prefixNetxTb.getId());
                if (prefixtb != null && prefixtb.getIssync() != null) {
                    Assert.isTrue(!prefixtb.getIssync(), "当前前缀由其他根节点同步，不允许修改");
                }
            }
        }
        prefixNetxTb.setUpdatedate(now);
        prefixNetxTb.setIsdelete(false);
        prefixNetxTbLongJpaRepository.saveAndFlush(prefixNetxTb);

        ioRepo.updateClause().set(qInstitutionsOrganizationTb.prefix, prefixNetxTb.getPrefixNext()).where(qInstitutionsOrganizationTb.id.eq(prefixNetxTb.getInstitutionsOrganizationTb().getId())).execute();
        // 当节点为根节点和原始根节点时，需要进行节点数据同步
        if (level == 0 || level == -1) {
            nodeSyncService.syncPrefixNode();
        }
    }

    /**
     * 保存更新
     *
     * @param data
     */
    @Transactional(rollbackFor = Exception.class)
    public PrefixNetxTb saveAndFlush(PrefixNetxTb data) {
        return prefixNetxTbLongJpaRepository.saveAndFlush(data);
    }

    /**
     * 根据前缀查询节点
     *
     * @param prefix
     * @return
     */
    public PrefixNetxTb findByPrefix(String prefix, Long id) {
        JPAQuery<PrefixNetxTb> query = prefixNetxTbLongJpaRepository.selectFrom(qPrefixNetxTb)
                .where(qPrefixNetxTb.isdelete.isFalse())
                .where(qPrefixNetxTb.prefixNext.eq(prefix));
        if (id != null && id > 0) {
            query.where(qPrefixNetxTb.id.ne(id));
        }
        return query.fetchFirst();
    }

    /**
     * 根据前缀查询节点
     *
     * @param syncId
     * @return
     */
    public PrefixNetxTb findByPrefixSyncId(Long syncId, String sourcePrefix) {
        JPAQuery<PrefixNetxTb> query = prefixNetxTbLongJpaRepository.selectFrom(qPrefixNetxTb)
                .where(qPrefixNetxTb.isdelete.isFalse())
                .where(qPrefixNetxTb.syncSourcePrefix.eq(sourcePrefix))
                .where(qPrefixNetxTb.syncId.eq(syncId));
        return query.fetchFirst();
    }

    /**
     * 根据前缀查询节点
     *
     * @param prefix
     * @return
     */
    public PrefixNetxTb findByPrefixCode(String prefix, String sourcePrefix) {
        JPAQuery<PrefixNetxTb> query = prefixNetxTbLongJpaRepository.selectFrom(qPrefixNetxTb)
                .where(qPrefixNetxTb.isdelete.isFalse())
                .where(qPrefixNetxTb.syncSourcePrefix.eq(sourcePrefix))
                .where(qPrefixNetxTb.prefixNext.eq(prefix));
        return query.fetchFirst();
    }

    @Transactional(rollbackFor = Exception.class)
    public void delete(Long id) {
        if (level == 0 || level == -1) {
            PrefixNetxTb prefix = prefixNetxTbLongJpaRepository.getOne(id);
            if (prefix != null && prefix.getIssync() != null) {
                Assert.isTrue(!prefix.getIssync(), "当前前缀由其他根节点同步，不允许删除");
            }
        }
        prefixNetxTbLongJpaRepository.updateClause().set(qPrefixNetxTb.isdelete, true).set(qPrefixNetxTb.updatedate, new Date()).where(qPrefixNetxTb.id.eq(id)).execute();
        // 当节点为根节点和原始根节点时，需要进行节点数据同步
        if (level == 0 || level == -1) {
            PrefixNetxTb prefix = prefixNetxTbLongJpaRepository.getOne(id);
            nodeSyncService.syncPrefixNode();
        }
    }

    public boolean checkHasPrefix(String prefix, Long id) {
        BooleanExpression expression = qPrefixNetxTb.prefixNext.eq(prefix)
                .and(qPrefixNetxTb.isdelete.isFalse())
                .and(qPrefixNetxTb.issync.isFalse());
        if (id != null) {
            expression = expression.and(qPrefixNetxTb.id.ne(id)).and(qPrefixNetxTb.issync.isFalse());
        }
        return prefixNetxTbLongJpaRepository.exists(expression);
    }

    public String getCurrentNodePrefix() {
        List<PrefixSelfTb> all = prefixSelfTbLongJpaRepository.findAll();

        if (CollectionUtils.isEmpty(all)) {
            return "";
        }

        return all.get(0).getPrefixSelf();
    }

    public boolean checkHasBindOrg(Long orgId, Long id) {
        BooleanExpression expression = qPrefixNetxTb.institutionsOrganizationTb.id.eq(orgId)
                .and(qPrefixNetxTb.isdelete.isFalse()).and(qPrefixNetxTb.issync.isFalse());
        if (id != null) {
            expression = expression.and(qPrefixNetxTb.id.ne(id)).and(qPrefixNetxTb.issync.isFalse());
        }
        Optional<InstitutionsOrganizationTb> optional = ioRepo.findById(orgId);
        return prefixNetxTbLongJpaRepository.exists(expression) || (optional.isPresent() && optional.get().getRootNodeId() != null);
    }
}
